/***************************************************************************/
/*                                                                         */
/*  otvcommn.c                                                             */
/*                                                                         */
/*    OpenType common tables validation (body).                            */
/*                                                                         */
/*  Copyright 2004, 2005, 2006, 2007 by                                    */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include "otvcommn.h"


/*************************************************************************/
/*                                                                       */
/* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
/* messages during execution.                                            */
/*                                                                       */
#undef  FT_COMPONENT
#define FT_COMPONENT trace_otvcommon


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                       COVERAGE TABLE                          *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

FT_LOCAL_DEF(void)
otv_Coverage_validate(FT_Bytes table,
                      OTV_Validator valid,
                      FT_Int expected_count)
{
    FT_Bytes p = table;
    FT_UInt CoverageFormat;
    FT_UInt total = 0;


    OTV_NAME_ENTER("Coverage");

    OTV_LIMIT_CHECK(4);
    CoverageFormat = FT_NEXT_USHORT(p);

    OTV_TRACE((" (format %d)\n", CoverageFormat));

    switch (CoverageFormat)
    {
        case 1: /* CoverageFormat1 */
        {
            FT_UInt GlyphCount;
            FT_UInt i;


            GlyphCount = FT_NEXT_USHORT(p);

            OTV_TRACE((" (GlyphCount = %d)\n", GlyphCount));

            OTV_LIMIT_CHECK(GlyphCount * 2);      /* GlyphArray */

            for (i = 0; i < GlyphCount; ++i)
            {
                FT_UInt gid;


                gid = FT_NEXT_USHORT(p);
                if (gid >= valid->glyph_count)
                    FT_INVALID_GLYPH_ID;
            }

            total = GlyphCount;
        }
        break;

        case 2: /* CoverageFormat2 */
        {
            FT_UInt n, RangeCount;
            FT_UInt Start, End, StartCoverageIndex, last = 0;


            RangeCount = FT_NEXT_USHORT(p);

            OTV_TRACE((" (RangeCount = %d)\n", RangeCount));

            OTV_LIMIT_CHECK(RangeCount * 6);

            /* RangeRecord */
            for (n = 0; n < RangeCount; n++)
            {
                Start = FT_NEXT_USHORT(p);
                End = FT_NEXT_USHORT(p);
                StartCoverageIndex = FT_NEXT_USHORT(p);

                if (Start > End || StartCoverageIndex != total)
                    FT_INVALID_DATA;

                if (End >= valid->glyph_count)
                    FT_INVALID_GLYPH_ID;

                if (n > 0 && Start <= last)
                    FT_INVALID_DATA;

                total += End - Start + 1;
                last = End;
            }
        }
        break;

        default:
            FT_INVALID_FORMAT;
    }

    /* Generally, a coverage table offset has an associated count field.  */
    /* The number of glyphs in the table should match this field.  If     */
    /* there is no associated count, a value of -1 tells us not to check. */
    if (expected_count != -1 && (FT_UInt)expected_count != total)
        FT_INVALID_DATA;

    OTV_EXIT;
}


FT_LOCAL_DEF(FT_UInt)
otv_Coverage_get_first(FT_Bytes table)
{
    FT_Bytes p = table;


    p += 4;     /* skip CoverageFormat and Glyph/RangeCount */

    return FT_NEXT_USHORT(p);
}


FT_LOCAL_DEF(FT_UInt)
otv_Coverage_get_last(FT_Bytes table)
{
    FT_Bytes p = table;
    FT_UInt CoverageFormat = FT_NEXT_USHORT(p);
    FT_UInt count = FT_NEXT_USHORT(p);                  /* Glyph/RangeCount */
    FT_UInt result = 0;


    switch (CoverageFormat)
    {
        case 1:
            p += (count - 1) * 2;
            result = FT_NEXT_USHORT(p);
            break;

        case 2:
            p += (count - 1) * 6 + 2;
            result = FT_NEXT_USHORT(p);
            break;

        default:
            ;
    }

    return result;
}


FT_LOCAL_DEF(FT_UInt)
otv_Coverage_get_count(FT_Bytes table)
{
    FT_Bytes p = table;
    FT_UInt CoverageFormat = FT_NEXT_USHORT(p);
    FT_UInt count = FT_NEXT_USHORT(p);                  /* Glyph/RangeCount */
    FT_UInt result = 0;


    switch (CoverageFormat)
    {
        case 1:
            return count;

        case 2:
        {
            FT_UInt Start, End;


            for (; count > 0; count--)
            {
                Start = FT_NEXT_USHORT(p);
                End = FT_NEXT_USHORT(p);
                p += 2;                 /* skip StartCoverageIndex */

                result += End - Start + 1;
            }
        }
        break;

        default:
            ;
    }

    return result;
}


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                   CLASS DEFINITION TABLE                      *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

FT_LOCAL_DEF(void)
otv_ClassDef_validate(FT_Bytes table,
                      OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt ClassFormat;


    OTV_NAME_ENTER("ClassDef");

    OTV_LIMIT_CHECK(4);
    ClassFormat = FT_NEXT_USHORT(p);

    OTV_TRACE((" (format %d)\n", ClassFormat));

    switch (ClassFormat)
    {
        case 1: /* ClassDefFormat1 */
        {
            FT_UInt StartGlyph;
            FT_UInt GlyphCount;


            OTV_LIMIT_CHECK(4);

            StartGlyph = FT_NEXT_USHORT(p);
            GlyphCount = FT_NEXT_USHORT(p);

            OTV_TRACE((" (GlyphCount = %d)\n", GlyphCount));

            OTV_LIMIT_CHECK(GlyphCount * 2);  /* ClassValueArray */

            if (StartGlyph + GlyphCount - 1 >= valid->glyph_count)
                FT_INVALID_GLYPH_ID;
        }
        break;

        case 2: /* ClassDefFormat2 */
        {
            FT_UInt n, ClassRangeCount;
            FT_UInt Start, End, last = 0;


            ClassRangeCount = FT_NEXT_USHORT(p);

            OTV_TRACE((" (ClassRangeCount = %d)\n", ClassRangeCount));

            OTV_LIMIT_CHECK(ClassRangeCount * 6);

            /* ClassRangeRecord */
            for (n = 0; n < ClassRangeCount; n++)
            {
                Start = FT_NEXT_USHORT(p);
                End = FT_NEXT_USHORT(p);
                p += 2;                     /* skip Class */

                if (Start > End || (n > 0 && Start <= last))
                    FT_INVALID_DATA;

                if (End >= valid->glyph_count)
                    FT_INVALID_GLYPH_ID;

                last = End;
            }
        }
        break;

        default:
            FT_INVALID_FORMAT;
    }

    /* no need to check glyph indices used as input to class definition   */
    /* tables since even invalid glyph indices return a meaningful result */

    OTV_EXIT;
}


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                      DEVICE TABLE                             *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

FT_LOCAL_DEF(void)
otv_Device_validate(FT_Bytes table,
                    OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt StartSize, EndSize, DeltaFormat, count;


    OTV_NAME_ENTER("Device");

    OTV_LIMIT_CHECK(8);
    StartSize = FT_NEXT_USHORT(p);
    EndSize = FT_NEXT_USHORT(p);
    DeltaFormat = FT_NEXT_USHORT(p);

    if (DeltaFormat < 1 || DeltaFormat > 3)
        FT_INVALID_FORMAT;

    if (EndSize < StartSize)
        FT_INVALID_DATA;

    count = EndSize - StartSize + 1;
    OTV_LIMIT_CHECK((1 << DeltaFormat) * count / 8);      /* DeltaValue */

    OTV_EXIT;
}


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                         LOOKUPS                               *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

/* uses valid->type_count */
/* uses valid->type_funcs */

FT_LOCAL_DEF(void)
otv_Lookup_validate(FT_Bytes table,
                    OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt LookupType, SubTableCount;
    OTV_Validate_Func validate;


    OTV_NAME_ENTER("Lookup");

    OTV_LIMIT_CHECK(6);
    LookupType = FT_NEXT_USHORT(p);
    p += 2;                                 /* skip LookupFlag */
    SubTableCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (type %d)\n", LookupType));

    if (LookupType == 0 || LookupType > valid->type_count)
        FT_INVALID_DATA;

    validate = valid->type_funcs[LookupType - 1];

    OTV_TRACE((" (SubTableCount = %d)\n", SubTableCount));

    OTV_LIMIT_CHECK(SubTableCount * 2);

    /* SubTable */
    for (; SubTableCount > 0; SubTableCount--)
        validate(table + FT_NEXT_USHORT(p), valid);

    OTV_EXIT;
}


/* uses valid->lookup_count */

FT_LOCAL_DEF(void)
otv_LookupList_validate(FT_Bytes table,
                        OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt LookupCount;


    OTV_NAME_ENTER("LookupList");

    OTV_LIMIT_CHECK(2);
    LookupCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (LookupCount = %d)\n", LookupCount));

    OTV_LIMIT_CHECK(LookupCount * 2);

    valid->lookup_count = LookupCount;

    /* Lookup */
    for (; LookupCount > 0; LookupCount--)
        otv_Lookup_validate(table + FT_NEXT_USHORT(p), valid);

    OTV_EXIT;
}


static FT_UInt
otv_LookupList_get_count(FT_Bytes table)
{
    return FT_NEXT_USHORT(table);
}


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                        FEATURES                               *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

/* uses valid->lookup_count */

FT_LOCAL_DEF(void)
otv_Feature_validate(FT_Bytes table,
                     OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt LookupCount;


    OTV_NAME_ENTER("Feature");

    OTV_LIMIT_CHECK(4);
    p += 2;                             /* skip FeatureParams (unused) */
    LookupCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (LookupCount = %d)\n", LookupCount));

    OTV_LIMIT_CHECK(LookupCount * 2);

    /* LookupListIndex */
    for (; LookupCount > 0; LookupCount--)
        if (FT_NEXT_USHORT(p) >= valid->lookup_count)
            FT_INVALID_DATA;

    OTV_EXIT;
}


static FT_UInt
otv_Feature_get_count(FT_Bytes table)
{
    return FT_NEXT_USHORT(table);
}


/* sets valid->lookup_count */

FT_LOCAL_DEF(void)
otv_FeatureList_validate(FT_Bytes table,
                         FT_Bytes lookups,
                         OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt FeatureCount;


    OTV_NAME_ENTER("FeatureList");

    OTV_LIMIT_CHECK(2);
    FeatureCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (FeatureCount = %d)\n", FeatureCount));

    OTV_LIMIT_CHECK(FeatureCount * 2);

    valid->lookup_count = otv_LookupList_get_count(lookups);

    /* FeatureRecord */
    for (; FeatureCount > 0; FeatureCount--)
    {
        p += 4;     /* skip FeatureTag */

        /* Feature */
        otv_Feature_validate(table + FT_NEXT_USHORT(p), valid);
    }

    OTV_EXIT;
}


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                       LANGUAGE SYSTEM                         *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/


/* uses valid->extra1 (number of features) */

FT_LOCAL_DEF(void)
otv_LangSys_validate(FT_Bytes table,
                     OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt ReqFeatureIndex;
    FT_UInt FeatureCount;


    OTV_NAME_ENTER("LangSys");

    OTV_LIMIT_CHECK(6);
    p += 2;                                 /* skip LookupOrder (unused) */
    ReqFeatureIndex = FT_NEXT_USHORT(p);
    FeatureCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (ReqFeatureIndex = %d)\n", ReqFeatureIndex));
    OTV_TRACE((" (FeatureCount = %d)\n", FeatureCount));

    if (ReqFeatureIndex != 0xFFFFU && ReqFeatureIndex >= valid->extra1)
        FT_INVALID_DATA;

    OTV_LIMIT_CHECK(FeatureCount * 2);

    /* FeatureIndex */
    for (; FeatureCount > 0; FeatureCount--)
        if (FT_NEXT_USHORT(p) >= valid->extra1)
            FT_INVALID_DATA;

    OTV_EXIT;
}


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                           SCRIPTS                             *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

FT_LOCAL_DEF(void)
otv_Script_validate(FT_Bytes table,
                    OTV_Validator valid)
{
    FT_UInt DefaultLangSys, LangSysCount;
    FT_Bytes p = table;


    OTV_NAME_ENTER("Script");

    OTV_LIMIT_CHECK(4);
    DefaultLangSys = FT_NEXT_USHORT(p);
    LangSysCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (LangSysCount = %d)\n", LangSysCount));

    if (DefaultLangSys != 0)
        otv_LangSys_validate(table + DefaultLangSys, valid);

    OTV_LIMIT_CHECK(LangSysCount * 6);

    /* LangSysRecord */
    for (; LangSysCount > 0; LangSysCount--)
    {
        p += 4;     /* skip LangSysTag */

        /* LangSys */
        otv_LangSys_validate(table + FT_NEXT_USHORT(p), valid);
    }

    OTV_EXIT;
}


/* sets valid->extra1 (number of features) */

FT_LOCAL_DEF(void)
otv_ScriptList_validate(FT_Bytes table,
                        FT_Bytes features,
                        OTV_Validator valid)
{
    FT_UInt ScriptCount;
    FT_Bytes p = table;


    OTV_NAME_ENTER("ScriptList");

    OTV_LIMIT_CHECK(2);
    ScriptCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (ScriptCount = %d)\n", ScriptCount));

    OTV_LIMIT_CHECK(ScriptCount * 6);

    valid->extra1 = otv_Feature_get_count(features);

    /* ScriptRecord */
    for (; ScriptCount > 0; ScriptCount--)
    {
        p += 4;     /* skip ScriptTag */

        otv_Script_validate(table + FT_NEXT_USHORT(p), valid);   /* Script */
    }

    OTV_EXIT;
}


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                      UTILITY FUNCTIONS                        *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

/*
   u:   uint16
   ux:  unit16 [x]

   s:   struct
   sx:  struct [x]
   sxy: struct [x], using external y count

   x:   uint16 x

   C:   Coverage

   O:   Offset
   On:  Offset (NULL)
   Ox:  Offset [x]
   Onx: Offset (NULL) [x]
 */

FT_LOCAL_DEF(void)
otv_x_Ox(FT_Bytes table,
         OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt Count;
    OTV_Validate_Func func;


    OTV_ENTER;

    OTV_LIMIT_CHECK(2);
    Count = FT_NEXT_USHORT(p);

    OTV_TRACE((" (Count = %d)\n", Count));

    OTV_LIMIT_CHECK(Count * 2);

    valid->nesting_level++;
    func = valid->func[valid->nesting_level];

    for (; Count > 0; Count--)
        func(table + FT_NEXT_USHORT(p), valid);

    valid->nesting_level--;

    OTV_EXIT;
}


FT_LOCAL_DEF(void)
otv_u_C_x_Ox(FT_Bytes table,
             OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt Count, Coverage;
    OTV_Validate_Func func;


    OTV_ENTER;

    p += 2;     /* skip Format */

    OTV_LIMIT_CHECK(4);
    Coverage = FT_NEXT_USHORT(p);
    Count = FT_NEXT_USHORT(p);

    OTV_TRACE((" (Count = %d)\n", Count));

    otv_Coverage_validate(table + Coverage, valid, Count);

    OTV_LIMIT_CHECK(Count * 2);

    valid->nesting_level++;
    func = valid->func[valid->nesting_level];

    for (; Count > 0; Count--)
        func(table + FT_NEXT_USHORT(p), valid);

    valid->nesting_level--;

    OTV_EXIT;
}


/* uses valid->extra1 (if > 0: array value limit) */

FT_LOCAL_DEF(void)
otv_x_ux(FT_Bytes table,
         OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt Count;


    OTV_ENTER;

    OTV_LIMIT_CHECK(2);
    Count = FT_NEXT_USHORT(p);

    OTV_TRACE((" (Count = %d)\n", Count));

    OTV_LIMIT_CHECK(Count * 2);

    if (valid->extra1)
    {
        for (; Count > 0; Count--)
            if (FT_NEXT_USHORT(p) >= valid->extra1)
                FT_INVALID_DATA;
    }

    OTV_EXIT;
}


/* `ux' in the function's name is not really correct since only x-1 */
/* elements are tested                                              */

/* uses valid->extra1 (array value limit) */

FT_LOCAL_DEF(void)
otv_x_y_ux_sy(FT_Bytes table,
              OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt Count1, Count2;


    OTV_ENTER;

    OTV_LIMIT_CHECK(4);
    Count1 = FT_NEXT_USHORT(p);
    Count2 = FT_NEXT_USHORT(p);

    OTV_TRACE((" (Count1 = %d)\n", Count1));
    OTV_TRACE((" (Count2 = %d)\n", Count2));

    if (Count1 == 0)
        FT_INVALID_DATA;

    OTV_LIMIT_CHECK((Count1 - 1) * 2 + Count2 * 4);
    p += (Count1 - 1) * 2;

    for (; Count2 > 0; Count2--)
    {
        if (FT_NEXT_USHORT(p) >= Count1)
            FT_INVALID_DATA;

        if (FT_NEXT_USHORT(p) >= valid->extra1)
            FT_INVALID_DATA;
    }

    OTV_EXIT;
}


/* `uy' in the function's name is not really correct since only y-1 */
/* elements are tested                                              */

/* uses valid->extra1 (array value limit) */

FT_LOCAL_DEF(void)
otv_x_ux_y_uy_z_uz_p_sp(FT_Bytes table,
                        OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt BacktrackCount, InputCount, LookaheadCount;
    FT_UInt Count;


    OTV_ENTER;

    OTV_LIMIT_CHECK(2);
    BacktrackCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (BacktrackCount = %d)\n", BacktrackCount));

    OTV_LIMIT_CHECK(BacktrackCount * 2 + 2);
    p += BacktrackCount * 2;

    InputCount = FT_NEXT_USHORT(p);
    if (InputCount == 0)
        FT_INVALID_DATA;

    OTV_TRACE((" (InputCount = %d)\n", InputCount));

    OTV_LIMIT_CHECK(InputCount * 2);
    p += (InputCount - 1) * 2;

    LookaheadCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (LookaheadCount = %d)\n", LookaheadCount));

    OTV_LIMIT_CHECK(LookaheadCount * 2 + 2);
    p += LookaheadCount * 2;

    Count = FT_NEXT_USHORT(p);

    OTV_TRACE((" (Count = %d)\n", Count));

    OTV_LIMIT_CHECK(Count * 4);

    for (; Count > 0; Count--)
    {
        if (FT_NEXT_USHORT(p) >= InputCount)
            FT_INVALID_DATA;

        if (FT_NEXT_USHORT(p) >= valid->extra1)
            FT_INVALID_DATA;
    }

    OTV_EXIT;
}


/* sets valid->extra1 (valid->lookup_count) */

FT_LOCAL_DEF(void)
otv_u_O_O_x_Onx(FT_Bytes table,
                OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt Coverage, ClassDef, ClassSetCount;
    OTV_Validate_Func func;


    OTV_ENTER;

    p += 2;     /* skip Format */

    OTV_LIMIT_CHECK(6);
    Coverage = FT_NEXT_USHORT(p);
    ClassDef = FT_NEXT_USHORT(p);
    ClassSetCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (ClassSetCount = %d)\n", ClassSetCount));

    otv_Coverage_validate(table + Coverage, valid, -1);
    otv_ClassDef_validate(table + ClassDef, valid);

    OTV_LIMIT_CHECK(ClassSetCount * 2);

    valid->nesting_level++;
    func = valid->func[valid->nesting_level];
    valid->extra1 = valid->lookup_count;

    for (; ClassSetCount > 0; ClassSetCount--)
    {
        FT_UInt offset = FT_NEXT_USHORT(p);


        if (offset)
            func(table + offset, valid);
    }

    valid->nesting_level--;

    OTV_EXIT;
}


/* uses valid->lookup_count */

FT_LOCAL_DEF(void)
otv_u_x_y_Ox_sy(FT_Bytes table,
                OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt GlyphCount, Count, count1;


    OTV_ENTER;

    p += 2;     /* skip Format */

    OTV_LIMIT_CHECK(4);
    GlyphCount = FT_NEXT_USHORT(p);
    Count = FT_NEXT_USHORT(p);

    OTV_TRACE((" (GlyphCount = %d)\n", GlyphCount));
    OTV_TRACE((" (Count = %d)\n", Count));

    OTV_LIMIT_CHECK(GlyphCount * 2 + Count * 4);

    for (count1 = GlyphCount; count1 > 0; count1--)
        otv_Coverage_validate(table + FT_NEXT_USHORT(p), valid, -1);

    for (; Count > 0; Count--)
    {
        if (FT_NEXT_USHORT(p) >= GlyphCount)
            FT_INVALID_DATA;

        if (FT_NEXT_USHORT(p) >= valid->lookup_count)
            FT_INVALID_DATA;
    }

    OTV_EXIT;
}


/* sets valid->extra1 (valid->lookup_count)    */

FT_LOCAL_DEF(void)
otv_u_O_O_O_O_x_Onx(FT_Bytes table,
                    OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt Coverage;
    FT_UInt BacktrackClassDef, InputClassDef, LookaheadClassDef;
    FT_UInt ChainClassSetCount;
    OTV_Validate_Func func;


    OTV_ENTER;

    p += 2;     /* skip Format */

    OTV_LIMIT_CHECK(10);
    Coverage = FT_NEXT_USHORT(p);
    BacktrackClassDef = FT_NEXT_USHORT(p);
    InputClassDef = FT_NEXT_USHORT(p);
    LookaheadClassDef = FT_NEXT_USHORT(p);
    ChainClassSetCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (ChainClassSetCount = %d)\n", ChainClassSetCount));

    otv_Coverage_validate(table + Coverage, valid, -1);

    otv_ClassDef_validate(table + BacktrackClassDef, valid);
    otv_ClassDef_validate(table + InputClassDef, valid);
    otv_ClassDef_validate(table + LookaheadClassDef, valid);

    OTV_LIMIT_CHECK(ChainClassSetCount * 2);

    valid->nesting_level++;
    func = valid->func[valid->nesting_level];
    valid->extra1 = valid->lookup_count;

    for (; ChainClassSetCount > 0; ChainClassSetCount--)
    {
        FT_UInt offset = FT_NEXT_USHORT(p);


        if (offset)
            func(table + offset, valid);
    }

    valid->nesting_level--;

    OTV_EXIT;
}


/* uses valid->lookup_count */

FT_LOCAL_DEF(void)
otv_u_x_Ox_y_Oy_z_Oz_p_sp(FT_Bytes table,
                          OTV_Validator valid)
{
    FT_Bytes p = table;
    FT_UInt BacktrackGlyphCount, InputGlyphCount, LookaheadGlyphCount;
    FT_UInt count1, count2;


    OTV_ENTER;

    p += 2;     /* skip Format */

    OTV_LIMIT_CHECK(2);
    BacktrackGlyphCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (BacktrackGlyphCount = %d)\n", BacktrackGlyphCount));

    OTV_LIMIT_CHECK(BacktrackGlyphCount * 2 + 2);

    for (; BacktrackGlyphCount > 0; BacktrackGlyphCount--)
        otv_Coverage_validate(table + FT_NEXT_USHORT(p), valid, -1);

    InputGlyphCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (InputGlyphCount = %d)\n", InputGlyphCount));

    OTV_LIMIT_CHECK(InputGlyphCount * 2 + 2);

    for (count1 = InputGlyphCount; count1 > 0; count1--)
        otv_Coverage_validate(table + FT_NEXT_USHORT(p), valid, -1);

    LookaheadGlyphCount = FT_NEXT_USHORT(p);

    OTV_TRACE((" (LookaheadGlyphCount = %d)\n", LookaheadGlyphCount));

    OTV_LIMIT_CHECK(LookaheadGlyphCount * 2 + 2);

    for (; LookaheadGlyphCount > 0; LookaheadGlyphCount--)
        otv_Coverage_validate(table + FT_NEXT_USHORT(p), valid, -1);

    count2 = FT_NEXT_USHORT(p);

    OTV_TRACE((" (Count = %d)\n", count2));

    OTV_LIMIT_CHECK(count2 * 4);

    for (; count2 > 0; count2--)
    {
        if (FT_NEXT_USHORT(p) >= InputGlyphCount)
            FT_INVALID_DATA;

        if (FT_NEXT_USHORT(p) >= valid->lookup_count)
            FT_INVALID_DATA;
    }

    OTV_EXIT;
}


FT_LOCAL_DEF(FT_UInt)
otv_GSUBGPOS_get_Lookup_count(FT_Bytes table)
{
    FT_Bytes p = table + 8;


    return otv_LookupList_get_count(table + FT_NEXT_USHORT(p));
}


FT_LOCAL_DEF(FT_UInt)
otv_GSUBGPOS_have_MarkAttachmentType_flag(FT_Bytes table)
{
    FT_Bytes p, lookup;
    FT_UInt count;


    if (!table)
        return 0;

    /* LookupList */
    p = table + 8;
    table += FT_NEXT_USHORT(p);

    /* LookupCount */
    p = table;
    count = FT_NEXT_USHORT(p);

    for (; count > 0; count--)
    {
        FT_Bytes oldp;


        /* Lookup */
        lookup = table + FT_NEXT_USHORT(p);

        oldp = p;

        /* LookupFlag */
        p = lookup + 2;
        if (FT_NEXT_USHORT(p) & 0xFF00U)
            return 1;

        p = oldp;
    }

    return 0;
}


/* END */